/*-------------------------------------------------------------------------
 *
 * fastpath.c
 *	  处理来自前端的函数请求的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/tcop/fastpath.c
 *
 * NOTES
 *	  这段杂项是 PQfn 的服务器端。
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_proc.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "port/pg_bswap.h"
#include "tcop/fastpath.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"


/*
 * 以前，这段代码试图缓存通过 fetch_fp_info 查找的函数和类型信息，
 * 但仅在单个事务命令的持续期间内（因为理论上信息可能在命令之间变化）。
 * 这是完全无用的，因为 postgres.c 将每个 fastpath 调用作为单独的事务命令执行，
 * 因此缓存的数据实际上从未被重用。如果它按预期工作，
 * 但由于 FmgrInfo 结构中的悬挂引用仍然会有问题。因此，忘记缓存，
 * 每次使用时只需重复 syscache 查找。它们并没有 *那么* 昂贵。
 */
struct fp_info
{
	Oid			funcid;
	FmgrInfo	flinfo;			/* funcid 的函数查找信息 */
	Oid			namespace;		/* 来自 pg_proc 的其他内容 */
	Oid			rettype;
	Oid			argtypes[FUNC_MAX_ARGS];
	char		fname[NAMEDATALEN]; /* 用于日志记录的函数名称 */
};


static int16 fc_parse_fcall_arguments(StringInfo fc_msgBuf, struct fp_info *fc_fip,
								   FunctionCallInfo fcinfo);

/* ----------------
 *		SendFunctionResult
 * ----------------
 */
static void fc_SendFunctionResult(Datum fc_retval, bool fc_isnull, Oid fc_rettype, int16 fc_format)
{
	StringInfoData fc_buf;

	pq_beginmessage(&fc_buf, 'V');

	if (fc_isnull)
	{
		pq_sendint32(&fc_buf, -1);
	}
	else
	{
		if (fc_format == 0)
		{
			Oid			fc_typoutput;
			bool		fc_typisvarlena;
			char	   *fc_outputstr;

			getTypeOutputInfo(fc_rettype, &fc_typoutput, &fc_typisvarlena);
			fc_outputstr = OidOutputFunctionCall(fc_typoutput, fc_retval);
			pq_sendcountedtext(&fc_buf, fc_outputstr, strlen(fc_outputstr), false);
			pfree(fc_outputstr);
		}
		else if (fc_format == 1)
		{
			Oid			fc_typsend;
			bool		fc_typisvarlena;
			bytea	   *fc_outputbytes;

			getTypeBinaryOutputInfo(fc_rettype, &fc_typsend, &fc_typisvarlena);
			fc_outputbytes = OidSendFunctionCall(fc_typsend, fc_retval);
			pq_sendint32(&fc_buf, VARSIZE(fc_outputbytes) - VARHDRSZ);
			pq_sendbytes(&fc_buf, VARDATA(fc_outputbytes),
						 VARSIZE(fc_outputbytes) - VARHDRSZ);
			pfree(fc_outputbytes);
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("unsupported format code: %d", fc_format)));
	}

	pq_endmessage(&fc_buf);
}

/*
 * fetch_fp_info
 *
 * 执行目录查找以加载函数 'func_id' 的结构 fp_info 'fip'。
 */
static void fc_fetch_fp_info(Oid fc_func_id, struct fp_info *fc_fip)
{
	HeapTuple	fc_func_htp;
	Form_pg_proc fc_pp;

	Assert(fc_fip != NULL);

	

/*
	 * 由于此结构的有效性取决于 funcid 是否有效，因此我们在这里清除 funcid。 
	 * 在我们准备返回一个有效的 struct fp_info 之前，它不得设置为正确的值， 
	 * 因为我们可能随时会被打断（即，通过 ereport(ERROR, ...)）. 
	 * [由于我们不再在事务之间保存 struct fp_info，所以这已经不再是一个问题， 
	 * 但仍然保留这一点。]
	 */
	MemSet(fc_fip, 0, sizeof(struct fp_info));
	fc_fip->funcid = InvalidOid;

	fc_func_htp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_func_id));
	if (!HeapTupleIsValid(fc_func_htp))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("function with OID %u does not exist", fc_func_id)));
	fc_pp = (Form_pg_proc) GETSTRUCT(fc_func_htp);

	/* 拒绝通过 fastpath 调用的 pg_proc 条目 */
	if (fc_pp->prokind != PROKIND_FUNCTION || fc_pp->proretset)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot call function \"%s\" via fastpath interface",
						NameStr(fc_pp->proname))));

	/* 注意有超过 FUNC_MAX_ARGS 参数的目录条目 */
	if (fc_pp->pronargs > FUNC_MAX_ARGS)
		elog(ERROR, "function %s has more than %d arguments",
			 NameStr(fc_pp->proname), FUNC_MAX_ARGS);

	fc_fip->namespace = fc_pp->pronamespace;
	fc_fip->rettype = fc_pp->prorettype;
	memcpy(fc_fip->argtypes, fc_pp->proargtypes.values, fc_pp->pronargs * sizeof(Oid));
	strlcpy(fc_fip->fname, NameStr(fc_pp->proname), NAMEDATALEN);

	ReleaseSysCache(fc_func_htp);

	fmgr_info(fc_func_id, &fc_fip->flinfo);

	/*
	 * 这必须是最后一个!
	 */
	fc_fip->funcid = fc_func_id;
}


/*
 * 处理函数请求
 *
 * PQfn 的服务器端（从前端的快速路径函数调用）。
 * 这对应于 libpq 协议符号 "F"。
 *
 * 输入：
 *		postgres.c 已经读取了消息体并将其传递给 msgBuf。
 *
 * 注意：这里和被调用函数中的 palloc() 不需要显式清理。
 * 我们是从 PostgresMain() 以 MessageContext 内存上下文被调用的，
 * 控制返回到 PostgresMain 时将自动重置。
 */
void HandleFunctionRequest(StringInfo fc_msgBuf)
{
	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
	Oid			fc_fid;
	AclResult	fc_aclresult;
	int16		fc_rformat;
	Datum		fc_retval;
	struct fp_info fc_my_fp;
	struct fp_info *fc_fip;
	bool		fc_callit;
	bool		fc_was_logged = false;
	char		fc_msec_str[32];

	/*
	 * 只有在处于中止事务时才接受 COMMIT/ABORT，
	 * 且不能通过快速路径接口执行 COMMIT/ABORT。
	 */
	if (IsAbortedTransactionBlockState())
		ereport(ERROR,
				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
				 errmsg("current transaction is aborted, "
						"commands ignored until end of transaction block")));

	/*
	 * 既然我们知道处于有效事务中，设置快照以备函数自身或
	 * 某个数据类型 I/O 例程需要。
	 */
	PushActiveSnapshot(GetTransactionSnapshot());

	/*
	 * 开始解析缓冲区内容。
	 */
	fc_fid = (Oid) pq_getmsgint(fc_msgBuf, 4);	/* 函数 oid */

	/*
	 * 这里曾经有一个无聊的尝试在此缓存查找信息。
	 * 现在我们在每次调用时都进行查找。
	 */
	fc_fip = &fc_my_fp;
	fc_fetch_fp_info(fc_fid, fc_fip);

	/* 一旦获得函数 OID 和名称，立即记录 */
	if (log_statement == LOGSTMT_ALL)
	{
		ereport(LOG,
				(errmsg("fastpath function call: \"%s\" (OID %u)",
						fc_fip->fname, fc_fid)));
		fc_was_logged = true;
	}

	/*
	 * 检查访问和调用函数的权限。
	 * 由于我们没有经过正常名称查找，因此还需要检查模式的使用。
	 */
	fc_aclresult = pg_namespace_aclcheck(fc_fip->namespace, GetUserId(), ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_SCHEMA,
					   get_namespace_name(fc_fip->namespace));
	InvokeNamespaceSearchHook(fc_fip->namespace, true);

	fc_aclresult = pg_proc_aclcheck(fc_fid, GetUserId(), ACL_EXECUTE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FUNCTION,
					   get_func_name(fc_fid));
	InvokeFunctionExecuteHook(fc_fid);

	/*
	 * 准备函数调用信息块并插入参数。
	 *
	 * 注意：目前我们传递 collation = InvalidOid，因此对区分排序的
	 * 函数不能以这种方式调用。也许我们应该传递 
	 * DEFAULT_COLLATION_OID？
	 */
	InitFunctionCallInfoData(*fcinfo, &fc_fip->flinfo, 0, InvalidOid, NULL, NULL);

	fc_rformat = fc_parse_fcall_arguments(fc_msgBuf, fc_fip, fcinfo);

	/* 验证我们是否在预期的位置到达消息的结尾。 */
	pq_getmsgend(fc_msgBuf);

	/*
	 * 如果函数是严格的，则不可对空参数调用。
	 */
	fc_callit = true;
	if (fc_fip->flinfo.fn_strict)
	{
		int			fc_i;

		for (fc_i = 0; fc_i < fcinfo->nargs; fc_i++)
		{
			if (fcinfo->args[fc_i].isnull)
			{
				fc_callit = false;
				break;
			}
		}
	}

	if (fc_callit)
	{
		/* 好的，开始吧…… */
		fc_retval = FunctionCallInvoke(fcinfo);
	}
	else
	{
		fcinfo->isnull = true;
		fc_retval = (Datum) 0;
	}

	/* 确保每次函数调用至少执行一次 CHECK_FOR_INTERRUPTS */
	CHECK_FOR_INTERRUPTS();

	fc_SendFunctionResult(fc_retval, fcinfo->isnull, fc_fip->rettype, fc_rformat);

	/* 我们不再需要快照 */
	PopActiveSnapshot();

	/*
	 * 如果合适则记录持续时间。
	 */
	switch (check_log_duration(fc_msec_str, fc_was_logged))
	{
		case 1:
			ereport(LOG,
					(errmsg("duration: %s ms", fc_msec_str)));
			break;
		case 2:
			ereport(LOG,
					(errmsg("duration: %s ms  fastpath function call: \"%s\" (OID %u)",
							fc_msec_str, fc_fip->fname, fc_fid)));
			break;
	}
}

/*
 * 在 3.0 协议消息中解析函数参数
 *
 * 参数值加载到 *fcinfo 中，所需的结果格式返回。
 */
static int16 fc_parse_fcall_arguments(StringInfo fc_msgBuf, struct fp_info *fc_fip,
					  FunctionCallInfo fcinfo)
{
	int			fc_nargs;
	int			fc_i;
	int			fc_numAFormats;
	int16	   *fc_aformats = NULL;
	StringInfoData fc_abuf;

	/* 获取参数格式代码 */
	fc_numAFormats = pq_getmsgint(fc_msgBuf, 2);
	if (fc_numAFormats > 0)
	{
		fc_aformats = (int16 *) palloc(fc_numAFormats * sizeof(int16));
		for (fc_i = 0; fc_i < fc_numAFormats; fc_i++)
			fc_aformats[fc_i] = pq_getmsgint(fc_msgBuf, 2);
	}

	fc_nargs = pq_getmsgint(fc_msgBuf, 2);	/* 参数数量 */

	if (fc_fip->flinfo.fn_nargs != fc_nargs || fc_nargs > FUNC_MAX_ARGS)
		ereport(ERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("function call message contains %d arguments but function requires %d",
						fc_nargs, fc_fip->flinfo.fn_nargs)));

	fcinfo->nargs = fc_nargs;

	if (fc_numAFormats > 1 && fc_numAFormats != fc_nargs)
		ereport(ERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("function call message contains %d argument formats but %d arguments",
						fc_numAFormats, fc_nargs)));

	initStringInfo(&fc_abuf);

	/*
	 * 将提供的参数复制到 arg 向量中。
	 */
	for (fc_i = 0; fc_i < fc_nargs; ++fc_i)
	{
		int			fc_argsize;
		int16		fc_aformat;

		fc_argsize = pq_getmsgint(fc_msgBuf, 4);
		if (fc_argsize == -1)
		{
			fcinfo->args[fc_i].isnull = true;
		}
		else
		{
			fcinfo->args[fc_i].isnull = false;
			if (fc_argsize < 0)
				ereport(ERROR,
						(errcode(ERRCODE_PROTOCOL_VIOLATION),
						 errmsg("invalid argument size %d in function call message",
								fc_argsize)));

			/* 重置 abuf 为空，并将原始数据插入其中 */
			resetStringInfo(&fc_abuf);
			appendBinaryStringInfo(&fc_abuf,
								   pq_getmsgbytes(fc_msgBuf, fc_argsize),
								   fc_argsize);
		}

		if (fc_numAFormats > 1)
			fc_aformat = fc_aformats[fc_i];
		else if (fc_numAFormats > 0)
			fc_aformat = fc_aformats[0];
		else
			fc_aformat = 0;		/* 默认 = 文本 */

		if (fc_aformat == 0)
		{
			Oid			fc_typinput;
			Oid			fc_typioparam;
			char	   *fc_pstring;

			getTypeInputInfo(fc_fip->argtypes[fc_i], &fc_typinput, &fc_typioparam);

			/*
			 * 由于 stringinfo.c 即使对于二进制数据也保持尾随的空字符，
			 * abuf 的内容是有效的 C 字符串。但在调用 typinput
			 * 例程之前，我们必须进行编码转换。
			 */
			if (fc_argsize == -1)
				fc_pstring = NULL;
			else
				fc_pstring = pg_client_to_server(fc_abuf.data, fc_argsize);

			fcinfo->args[fc_i].value = OidInputFunctionCall(fc_typinput, fc_pstring,
														 fc_typioparam, -1);
			/* 释放编码转换的结果（如果有） */
			if (fc_pstring && fc_pstring != fc_abuf.data)
				pfree(fc_pstring);
		}
		else if (fc_aformat == 1)
		{
			Oid			fc_typreceive;
			Oid			fc_typioparam;
			StringInfo	fc_bufptr;

			/* 调用参数类型的二进制输入转换器 */
			getTypeBinaryInputInfo(fc_fip->argtypes[fc_i], &fc_typreceive, &fc_typioparam);

			if (fc_argsize == -1)
				fc_bufptr = NULL;
			else
				fc_bufptr = &fc_abuf;

			fcinfo->args[fc_i].value = OidReceiveFunctionCall(fc_typreceive, fc_bufptr,
														   fc_typioparam, -1);

			/* 如果没有读取完整个缓冲区则会出问题 */
			if (fc_argsize != -1 && fc_abuf.cursor != fc_abuf.len)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
						 errmsg("incorrect binary data format in function argument %d",
								fc_i + 1)));
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("unsupported format code: %d", fc_aformat)));
	}

	/* 返回结果格式代码 */
	return (int16) pq_getmsgint(fc_msgBuf, 2);
}
